
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

%{
#include <assert.h>
#include <iostream>
//#include <gc.h>
#include <vector>
#include <stdio.h>
#include "ArgumentListExpression.h"
#include "ArrayExpression.h"
#include "BinaryExpression.h"
#include "Block.h"
#include "Boolean.h"
#include "CaseClause.h"
#include "CaseClauses.h"
#include "CatchBlock.h"
#include "CompletionStatement.h"
#include "ConditionalExpression.h"
#include "DoWhileStatement.h"
#include "Expression.h"
#include "ExpressionStatement.h"
#include "ForInStatement.h"
#include "ForStatement.h"
#include "FormalParameterList.h"
#include "FunctionDeclaration.h"
#include "FunctionExpression.h"
#include "IfStatement.h"
#include "IfElseStatement.h"
#include "InternalFunction.h"
#include "NameValue.h"
#include "NameValueList.h"
#include "Number.h"
#include "Object.h"
#include "ObjectExpression.h"
#include "Reference.h"
#include "Scope.h"
#include "SourceElement.h"
#include "SourceElements.h"
#include "Statement.h"
#include "String.h"
#include "SwitchStatement.h"
#include "ThisExpression.h"
#include "TryStatement.h"
#include "UnaryExpression.h"
#include "Undefined.h"
#include "VariableStatement.h"
#include "WithStatement.h"

//#define YYMALLOC GC_MALLOC
#define YYFREE (void)

int yylex (void);
int yyerror(SourceElements** prog, const char* s);

extern Scope* global_scope;
void expect_regex();
String* curr_file = new String("???");
%}

%locations
%parse-param { SourceElements** prog }
%debug

%union {
    binary_operation_t binop;
    CaseClause* cclause;
    CaseClauses* cclauses;
    CatchBlock* cblock;
    Expression* expression;
    FormalParameterList* param_list;
    FunctionDeclaration* func_dec;
    NameValue* name_val;
    NameValueList* name_val_list;
    SourceElements* src_elems;
    SourceElement* src_elem;
    Statement* statement;
    String* string;
    Value* value;
}


%token IF ELSE LTE GTE DEQ NEQ WHILE FUNCTION RETURN TYPEOF INC DEC VOID
%token TRY CATCH FINALLY THROW VAR FOR THIS BREAK NEW SDEQ SNEQ
%token INSTANCEOF DELETE DBAR DAMP LEFT_SHIFT RIGHT_SHIFT UNSIGNED_RIGHT_SHIFT
%token IN CONTINUE WITH DO SWITCH CASE DEFAULT SYNCHRONIZED
%token <binop> ASSIGNMENT_OPERATOR
%token <expression> REGEX_LITERAL;
%token <string> IDENTIFIER
%token <value> LITERAL

%type <cclause> CaseClause
%type <cclauses> CaseClauses
%type <cblock> Catch
%type <expression> Expression AssignmentExpression ConditionalExpression
%type <expression> LogicalORExpression LogicalANDExpression BitwiseORExpression
%type <expression> BitwiseXORExpression BitwiseANDExpression EqualityExpression
%type <expression> RelationalExpression ShiftExpression AdditiveExpression
%type <expression> MultiplicativeExpression UnaryExpression PostfixExpression
%type <expression> LeftHandSideExpression CallExpression NewExpression
%type <expression> MemberExpression PrimaryExpression ForExpression
%type <expression> Arguments ArgumentList Initializer FunctionExpression
%type <expression> ArrayLiteral ObjectLiteral ElementList Elision
%type <func_dec> FunctionDeclaration
%type <name_val> VariableDeclaration
%type <name_val_list> VariableDeclarationList PropertyNameAndValueList
%type <param_list> FormalParameterList
%type <src_elem> SourceElement
%type <src_elems> SourceElements FunctionBody
%type <statement> Statement ExpressionStatement ReturnStatement
%type <statement> IfStatement IterationStatement Block StatementList
%type <statement> TryStatement Finally ThrowStatement VariableStatement
%type <statement> BreakStatement ContinueStatement EmptyStatement
%type <statement> WithStatement LabelledStatement SwitchStatement CaseBlock
%type <statement> DefaultClause
%type <string> PropertyName

%start Program

%%

Program
    : SourceElements { *prog = $1;}
    ;

SourceElements
    : SourceElement { $$ = new SourceElements($1); $$->setFile(curr_file); }
    | SourceElements SourceElement { $1->push_back($2); $$ = $1;}
    ;

SourceElement
    : Statement {$$ = new SourceElement($1);}
    | FunctionDeclaration { $$ = new SourceElement($1);}
    ;

FunctionDeclaration
    : FUNCTION IDENTIFIER '(' ')' '{' FunctionBody '}'
        {
            $$ = new FunctionDeclaration($2, new FormalParameterList(), $6, 0);
        }
    | FUNCTION IDENTIFIER '(' FormalParameterList ')' '{' FunctionBody '}'
        {
            $$ = new FunctionDeclaration($2, $4, $7, 0);
        }
	| SYNCHRONIZED FUNCTION IDENTIFIER '(' FormalParameterList ')' '{' FunctionBody '}'
        {
            $$ = new FunctionDeclaration($3, $5, $8, 1);
        }
	| SYNCHRONIZED FUNCTION IDENTIFIER '(' ')' '{' FunctionBody '}'
        {
            $$ = new FunctionDeclaration($3, new FormalParameterList(), $7, 1);
        }
    ;

FunctionExpression
    : FUNCTION '(' FormalParameterList ')' '{' FunctionBody '}'
        {
            $$ = new FunctionExpression(NULL, $3, $6, 0);
            $6->setLineNumber(@1.last_line);
        }
    | FUNCTION '(' ')' '{' FunctionBody '}'
        {
            $$ = new FunctionExpression(NULL, new FormalParameterList(), $5, 0);
            $5->setLineNumber(@1.last_line);
        }
    | FUNCTION IDENTIFIER '(' FormalParameterList ')' '{' FunctionBody '}'
        {
            $$ = new FunctionExpression($2, $4, $7, 0);
            $7->setLineNumber(@1.last_line);
        }
    | FUNCTION IDENTIFIER '(' ')' '{' FunctionBody '}'
        {
            $$ = new FunctionExpression($2, new FormalParameterList(), $6, 0);
            $6->setLineNumber(@1.last_line);
        }
	| SYNCHRONIZED FUNCTION '(' FormalParameterList ')' '{' FunctionBody '}'
        {
            $$ = new FunctionExpression(NULL, $4, $7, 1);
            $7->setLineNumber(@1.last_line);
        }
    | SYNCHRONIZED FUNCTION '(' ')' '{' FunctionBody '}'
        {
            $$ = new FunctionExpression(NULL, new FormalParameterList(), $6, 1);
            $6->setLineNumber(@1.last_line);
        }
    | SYNCHRONIZED FUNCTION IDENTIFIER '(' FormalParameterList ')' '{' FunctionBody '}'
        {
            $$ = new FunctionExpression($3, $5, $8, 1);
            $8->setLineNumber(@1.last_line);
        }
    | SYNCHRONIZED FUNCTION IDENTIFIER '(' ')' '{' FunctionBody '}'
        {
            $$ = new FunctionExpression($3, new FormalParameterList(), $7, 1);
            $7->setLineNumber(@1.last_line);
        }
    ;

FunctionBody
    : SourceElements { $$ = $1; }
    | /* empty */   
        { 
            // This is a case of auto-semicolon insertion. We insert
            // an empty statement.
            Statement* empty = new CompletionStatement(CT_NORMAL, NULL,
                                                            Completion::empty);
            empty->setLineNumber(@$.last_line);
            $$ = new SourceElements(new SourceElement(empty));
            $$->setFile(curr_file);
        }
    ;

FormalParameterList
    : IDENTIFIER { $$ = new FormalParameterList($1); }
    | FormalParameterList ',' IDENTIFIER { $1->push_back($3); $$=$1;}
    ;

Statement
    : Block                 { $$ = $1; }
    | VariableStatement     { $$ = $1; }
    | EmptyStatement        { $$ = $1; }
    | ExpressionStatement   { $$ = $1; }
    | IfStatement           { $$ = $1; }
    | IterationStatement    { $$ = $1; }
    | ContinueStatement     { $$ = $1; }
    | BreakStatement        { $$ = $1; }
    | ReturnStatement       { $$ = $1; }
    | WithStatement         { $$ = $1; }
    | SwitchStatement       { $$ = $1; }
    | LabelledStatement     { $$ = $1; }
    | ThrowStatement        { $$ = $1; }
    | TryStatement          { $$ = $1; }
    ;

Block
    : '{' '}' { $$ = new Block(); ((Block*)$$)->set_sync(0); $$->setLineNumber(@$.last_line);}
    | '{' StatementList '}' { $$ = $2; ((Block*)$$)->set_sync(0); $$->setLineNumber(@$.last_line);}
	| SYNCHRONIZED '(' IDENTIFIER ')' '{' '}' { $$ = new Block(); ((Block*)$$)->set_sync(1);  ((Block*)$$)->set_id($3) ; $$->setLineNumber(@$.last_line);}
    | SYNCHRONIZED '(' IDENTIFIER ')' '{' StatementList '}' { $$ = $6;  ((Block*)$$)->set_sync(1);  ((Block*)$$)->set_id($3) ; $$->setLineNumber(@$.last_line);}
	| SYNCHRONIZED '(' THIS ')' '{' '}' { $$ = new Block(); ((Block*)$$)->set_sync(1);  ((Block*)$$)->set_this(1);$$->setLineNumber(@$.last_line);}
    | SYNCHRONIZED '(' THIS ')' '{' StatementList '}' { $$ = $6;  ((Block*)$$)->set_sync(1); ((Block*)$$)->set_this(1); $$->setLineNumber(@$.last_line);}
    ;

StatementList
    : Statement 
        {
            $$ = new Block();
            ((Block*)$$)->push_back($1);
            $$->setLineNumber(@$.last_line);
        }
    | StatementList Statement
        { 
            ((Block*)$1)->push_back($2);
            $$ = $1;
            $$->setLineNumber(@$.last_line);
        }
    ;

VariableStatement
    : VAR VariableDeclarationList ';' 
        {
            $$ = new VariableStatement($2);
            $$->setLineNumber(@$.last_line);
        }
    | VAR VariableDeclarationList error
        {
            // Attemp automatic semicolon insertion
            // TODO: make this right
            fprintf(stderr, "Inserting semicolon (Var Stmt)\n");
            $$ = new VariableStatement($2);
            $$->setLineNumber(@$.last_line);
            yyerrok;
        }
    ;

VariableDeclarationList
    : VariableDeclaration { $$ = new NameValueList($1); }
    | VariableDeclarationList ',' VariableDeclaration
        {
            $$ = $1; $1->push_back($3);
        }
    ;

VariableDeclaration
    : IDENTIFIER                { $$ = new NameValue($1, NULL); }
    | IDENTIFIER Initializer    { $$ = new NameValue($1, $2); }
    ;

Initializer
    : '=' AssignmentExpression  { $$ = $2; }
    ;

EmptyStatement
    : ';'   
        {
            $$ = new CompletionStatement(CT_NORMAL, NULL, Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    ;

ExpressionStatement
    : Expression ';'    
        {
            $$ = new ExpressionStatement($1);
            $$->setLineNumber(@$.last_line);
        }
    | Expression error
        {
            // We might be able to insert a semicolon now and keep going.
            // The rule is: We can insert a semicolon if the offending
            // token is separated from the previous token by at least
            // one LineTerminator, or the offending token is '}'
            // TODO: make this right
            fprintf(stderr, "Inserting semicolon (ExpressionStmt)\n");
            $$ = new ExpressionStatement($1);
            $$->setLineNumber(@$.last_line);
            yyerrok;
        }
    ;

IfStatement
    : IF '(' Expression ')' Statement ELSE Statement
        {
            $$ = new IfElseStatement($3, $5, $7);
            $$->setLineNumber(@$.last_line);
        }
    | IF '(' Expression ')' Statement 
        {
            $$ = new IfStatement($3, $5);
            $$->setLineNumber(@$.last_line);
        }
    ;

IterationStatement
    : DO Statement WHILE '(' Expression ')' ';'
        {
            $$ = new DoWhileStatement($2, $5);
            $$->addLabel(Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    | WHILE '(' Expression ')' Statement 
        {
            $$ = new ForStatement(NULL, NULL, $3, NULL, $5);
            $$->addLabel(Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    | FOR '(' ForExpression ';' ForExpression ';' ForExpression ')' Statement
        {
            $$ = new ForStatement(NULL, $3, $5, $7, $9);
            $$->addLabel(Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    | FOR '(' VAR VariableDeclarationList ';' ForExpression ';' ForExpression ')' Statement
        {
            $$ = new ForStatement($4, NULL, $6, $8, $10);
            $$->addLabel(Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    | FOR '(' LeftHandSideExpression IN Expression ')' Statement
        {
            $$ = new ForInStatement(NULL, $3, $5, $7);
            $$->addLabel(Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    | FOR '(' VAR VariableDeclaration IN Expression ')' Statement
        {
            $$ = new ForInStatement($4, NULL, $6, $8);
            $$->addLabel(Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    ;

ForExpression
    :   /*empty*/   { $$ = NULL; }
    | Expression    { $$ = $1; }
    ;

ContinueStatement
    : CONTINUE ';'  
        {
            $$ = new CompletionStatement(CT_CONTINUE, NULL, Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    | CONTINUE IDENTIFIER ';'
        {
            $$ = new CompletionStatement(CT_CONTINUE, NULL, $2);
            $$->setLineNumber(@$.last_line);
        }
    ;

BreakStatement
    : BREAK ';'     
        {
            $$ = new CompletionStatement(CT_BREAK, NULL, Completion::empty);
            $$->setLineNumber(@$.last_line);
        } 
    | BREAK IDENTIFIER ';'
        {
            $$ = new CompletionStatement(CT_BREAK, NULL, $2);
            $$->setLineNumber(@$.last_line);
        }
    ;

ReturnStatement
    : RETURN ';'
        {
            $$ = new CompletionStatement(CT_RETURN, Undefined::undefined,
                            Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    | RETURN Expression ';'
        {
            $$ = new CompletionStatement(CT_RETURN, $2, Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    ;

WithStatement
    : WITH '(' Expression ')' Statement { $$ = new WithStatement($3, $5); }
    ;

SwitchStatement
    : SWITCH '(' Expression ')' CaseBlock
        {
            $$ = $5;
            ((SwitchStatement*)$$)->set_expression($3);
            $$->setLineNumber(@$.last_line);
        }
    ;

CaseBlock
    : '{' '}'               { $$ = new SwitchStatement(NULL, NULL, NULL); }
    | '{' CaseClauses '}'   { $$ = new SwitchStatement($2, NULL, NULL); }
    | '{' DefaultClause '}' { $$ = new SwitchStatement(NULL, $2, NULL); }
    | '{' CaseClauses DefaultClause '}'
        {
            $$ = new SwitchStatement($2, $3, NULL);
        }
    | '{' DefaultClause CaseClauses '}'
        {
            $$ = new SwitchStatement(NULL, $2, $3);
        }
    | '{' CaseClauses DefaultClause CaseClauses '}'
        {
            $$ = new SwitchStatement($2, $3, $4);
        }
    ;

CaseClauses
    : CaseClause                { $$ = new CaseClauses($1); }
    | CaseClauses CaseClause    { $$ = $1; $1->push_back($2); }
    ;

CaseClause
    : CASE Expression ':'               { $$ = new CaseClause($2, NULL); }
    | CASE Expression ':' StatementList { $$ = new CaseClause($2, $4); }
    ;

DefaultClause
    : DEFAULT ':'                   { $$ = NULL; }
    | DEFAULT ':' StatementList     { $$ = $3; }
    ;

LabelledStatement
    : IDENTIFIER ':' Statement { $$ = $3; $$->addLabel($1); }
    ;

ThrowStatement
    : THROW Expression ';'
        {
            $$ = new CompletionStatement(CT_THROW, $2, Completion::empty);
            $$->setLineNumber(@$.last_line);
        }
    ;

TryStatement
    : TRY Block Catch           
        {
            $$ = new TryStatement($2, $3, NULL);
            $$->setLineNumber(@$.last_line);
        }
    | TRY Block Finally         
        {
            $$ = new TryStatement($2, NULL, $3);
            $$->setLineNumber(@$.last_line);
        }
    | TRY Block Catch Finally   
        {
            $$ = new TryStatement($2, $3, $4);
            $$->setLineNumber(@$.last_line);
        }
    ;

Catch
    : CATCH '(' IDENTIFIER ')' Block { $$ = new CatchBlock($3, $5); }
    ;

Finally
    : FINALLY Block { $$ = $2; }
    ;

Expression
    : AssignmentExpression                  { $$ = $1; }
    | Expression ',' AssignmentExpression   
        {
            $$ = new BinaryExpression(&BinaryExpression::comma, $1, $3);
        }
    ;

AssignmentExpression
    : ConditionalExpression {$$ = $1;}
    | LeftHandSideExpression '=' AssignmentExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::assign, $1, $3);
        }
    | LeftHandSideExpression ASSIGNMENT_OPERATOR AssignmentExpression
        {
            $$ = new BinaryExpression($2, $1, $3);
        } 
    ;

ConditionalExpression
    : LogicalORExpression {$$ = $1;}
    | LogicalORExpression '?' AssignmentExpression ':' AssignmentExpression
        {
            $$ = new ConditionalExpression($1, $3, $5);
        }
    ;

LogicalORExpression
    : LogicalANDExpression {$$ = $1;}
    | LogicalORExpression DBAR LogicalANDExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::logical_or, $1, $3);
        }
    ;

LogicalANDExpression
    : BitwiseORExpression {$$ = $1;}
    | LogicalANDExpression DAMP BitwiseORExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::logical_and, $1, $3);
        }
    ;

BitwiseORExpression
    : BitwiseXORExpression {$$ = $1;}
    | BitwiseORExpression '|' BitwiseXORExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::bitwise_or, $1, $3);
        }
    ;

BitwiseXORExpression
    : BitwiseANDExpression {$$ = $1;}
    | BitwiseXORExpression '^' BitwiseANDExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::bitwise_xor, $1, $3);
        }
    ;

BitwiseANDExpression
    : EqualityExpression {$$ = $1;}
    | BitwiseANDExpression '&' EqualityExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::bitwise_and, $1, $3);
        }
    ;

EqualityExpression
    : RelationalExpression {$$ = $1;}
    | EqualityExpression DEQ RelationalExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::equals, $1, $3);
        }
    | EqualityExpression NEQ RelationalExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::not_equals, $1, $3);
        }
    | EqualityExpression SDEQ RelationalExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::strict_equals, $1, $3);
        }
    | EqualityExpression SNEQ RelationalExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::strict_not_equals, $1, $3);
        }
    ;

RelationalExpression
    : ShiftExpression {$$ = $1;}
    | RelationalExpression '<' ShiftExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::less_than, $1, $3);
        }
    | RelationalExpression '>' ShiftExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::greater_than, $1, $3);
        }
    | RelationalExpression LTE ShiftExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::less_than_equal, $1, $3);
        }
    | RelationalExpression GTE ShiftExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::greater_than_equal, $1, $3);
        }
    | RelationalExpression INSTANCEOF ShiftExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::instanceof, $1, $3);
        }
    | RelationalExpression IN ShiftExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::in, $1, $3);
        }
    ;

ShiftExpression
    : AdditiveExpression {$$ = $1;}
    | ShiftExpression LEFT_SHIFT AdditiveExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::left_shift, $1, $3);
        }
    | ShiftExpression RIGHT_SHIFT AdditiveExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::right_shift, $1, $3);
        }
    | ShiftExpression UNSIGNED_RIGHT_SHIFT AdditiveExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::unsigned_right_shift, $1, $3);
        }
    ;

AdditiveExpression
    : MultiplicativeExpression {$$ = $1;}
    | AdditiveExpression '+' MultiplicativeExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::add, $1, $3);
        }
    | AdditiveExpression '-' MultiplicativeExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::subtract, $1, $3);
        }
    ;

MultiplicativeExpression
    : UnaryExpression {$$ = $1;}
    | MultiplicativeExpression '*' UnaryExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::multiply, $1, $3);
        }
    | MultiplicativeExpression '/' UnaryExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::divide, $1, $3);
        }
    | MultiplicativeExpression '%' UnaryExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::modulo, $1, $3);
        }
    ;

UnaryExpression
    : PostfixExpression         { $$ = $1;}
    | DELETE UnaryExpression    
        {
            $$ = new UnaryExpression(&UnaryExpression::delete_op, $2);
        }
    | VOID UnaryExpression      
        {
            $$ = new UnaryExpression(&UnaryExpression::void_op, $2);
        }
    | TYPEOF UnaryExpression    
        {
            $$ = new UnaryExpression(&UnaryExpression::typeof_op, $2);
        }
    | INC UnaryExpression       
        {
            $$ = new UnaryExpression(&UnaryExpression::pre_increment, $2);
        }
    | DEC UnaryExpression       
        {
            $$ = new UnaryExpression(&UnaryExpression::pre_decrement, $2);
        }
    | '+' UnaryExpression
        {
            $$ = new UnaryExpression(&UnaryExpression::plus, $2);
        }
    | '-' UnaryExpression       
        {
            $$ = new UnaryExpression(&UnaryExpression::minus, $2);
        }
    | '~' UnaryExpression
        {
            $$ = new UnaryExpression(&UnaryExpression::bitwise_not, $2);
        }
    | '!' UnaryExpression       
        {
            $$ = new UnaryExpression(&UnaryExpression::logical_not, $2);
        }
    ;

PostfixExpression
    : LeftHandSideExpression        { $$ = $1;}
    | LeftHandSideExpression INC    
        {
            $$ = new UnaryExpression(&UnaryExpression::post_increment, $1);
        }
    | LeftHandSideExpression DEC    
        {
            $$ = new UnaryExpression(&UnaryExpression::post_decrement, $1);
        }
    ;

LeftHandSideExpression
    : NewExpression     {$$ = $1;}
    | CallExpression    {$$ = $1;}
    ;

CallExpression
    : MemberExpression Arguments        
        {
            $$ = new BinaryExpression(&BinaryExpression::call, $1, $2);
        }
    | CallExpression Arguments          
        {
            $$ = new BinaryExpression(&BinaryExpression::call, $1, $2);
        }
    | CallExpression '[' Expression ']'
        {
            $$ = new BinaryExpression(&BinaryExpression::lookup, $1, $3);
        }
    | CallExpression '.' IDENTIFIER     
        {
            $$ = new BinaryExpression(&BinaryExpression::lookup, $1, $3);
        }
    ;

NewExpression
    : MemberExpression  { $$ = $1;}
    | NEW NewExpression
        {
            $$ = new BinaryExpression(&BinaryExpression::construct, $2, NULL);
        }
    ;

MemberExpression
    : PrimaryExpression                   { $$ = $1;}
    | FunctionExpression                  { $$ = $1;}
    | MemberExpression '[' Expression ']'
        {
            $$ = new BinaryExpression(&BinaryExpression::lookup, $1, $3);
        }
    | MemberExpression '.' IDENTIFIER     
        {
            $$ = new BinaryExpression(&BinaryExpression::lookup, $1, $3);
        }
    | NEW MemberExpression Arguments
        {
            $$ = new BinaryExpression(&BinaryExpression::construct, $2, $3);
        }
    ;

PrimaryExpression
    : THIS                  { $$ = new ThisExpression();}
    | IDENTIFIER            
        {
            $$ = new UnaryExpression(&UnaryExpression::identifier, $1);
        }
    | LITERAL               { $$ = $1; }
    | {
        expect_regex();
      } '/' REGEX_LITERAL { $$ = $3; }
    | ArrayLiteral          { $$ = $1; }
    | ObjectLiteral         { $$ = $1; }
    | '(' Expression ')'    { $$ = $2; }
    ;

ArrayLiteral
    : '[' ']'                   { $$ = new ArrayExpression(); }
    | '[' Elision ']'           { $$ = $2; }
    | '[' ElementList ']'       { $$ = $2; }
    | '[' ElementList ',' ']'   { $$ = $2; }
    | '[' ElementList ',' Elision ']'
        {
            $$ = $2; ((ArrayExpression*)$$)->append((ArrayExpression*)$4);
        }
    ;

ElementList
    : AssignmentExpression          { $$ = new ArrayExpression($1); }
    | Elision AssignmentExpression   
        {
            $$ = $1;
            ((ArrayExpression*)$$)->push_back($2);
        }
    | ElementList ',' AssignmentExpression
        {
            $$ = $1;
            ((ArrayExpression*)$$)->push_back($3);
        }
    | ElementList ',' Elision AssignmentExpression
        {
            $$ = $1;
            ((ArrayExpression*)$$)->append((ArrayExpression*)$3);
            ((ArrayExpression*)$$)->push_back($4);
        }
    ;

Elision
    : ','           { $$ = new ArrayExpression(NULL); }
    | Elision ','    { $$ = $1; ((ArrayExpression*)$$)->push_back(NULL); } 
    ;

ObjectLiteral
    : '{' '}'       { $$ = new ObjectExpression(new NameValueList()); }
    | '{' PropertyNameAndValueList '}' { $$ = new ObjectExpression($2); }
    ;

PropertyNameAndValueList
    : PropertyName ':' AssignmentExpression
        {
            $$ = new NameValueList(new NameValue($1, $3));
        }
    | PropertyNameAndValueList ',' PropertyName ':' AssignmentExpression
        {
            $$ = $1;
            $$->push_back(new NameValue($3, $5));
        }
    ;

PropertyName
    : IDENTIFIER    { $$ = $1; }
    | LITERAL       { $$ = $1->toString(); }
    ;

Arguments
    : '(' ')'              { $$ = new ArgumentListExpression(); }
    | '(' ArgumentList ')' { $$ = $2;}
    ;

ArgumentList
    : AssignmentExpression { $$ = new ArgumentListExpression($1);}
    | ArgumentList ',' AssignmentExpression
        {
            $$ = $1; ((ArgumentListExpression*)$1)->push_back($3);
        }
    ;


%%

#include <stdio.h>

int yyerror(SourceElements** prog, const char* s)
{
    // The runtime will report the syntax error, no need to do so here.
    //fprintf(stderr, "%s around line %i\n", s, yylloc.last_line);
    return 0;
}

// vim: filetype=yacc
